#include "OriginPCH.h"
#include "WindowsWindow.h"
#include "Core/Events/ApplicationEvent.h"
#include "Core/Events/WindowEvent.h"
#include "Core/Events/KeyEvent.h"
#include "Core/Events/MouseEvent.h"
#include "Platform/OpenGL/OpenGLContext.h"


namespace Origin
{
    static bool _glfwInitialized;

    static void GLFWErrorCallback(int error, const char* description)
    {
        ORIGIN_CORE_ERROR("GLFW Error ({0}): {1}", error, description);
    }

    Window* Window::Create(const WindowProps& props)
    {
        // 通过动态内存创建的就是指针
        return new WindowsWindow(props);
    }

    WindowsWindow::WindowsWindow(const WindowProps& props)
    {
        Init(props);
    }

    WindowsWindow::~WindowsWindow()
    {
        Shutdown();
    }

    void WindowsWindow::Init(const WindowProps& props)
    {
        if (!_glfwInitialized)
        {
            int success = glfwInit();
            ORIGIN_CORE_ASSERT(success, "Could not intialize GLFW!");
            glfwSetErrorCallback(GLFWErrorCallback);
            _glfwInitialized = true;
        }

        _pointerData.width = props.width;
        _pointerData.height = props.height;
        _pointerData.title = props.title;

        _glfwWindow = glfwCreateWindow(props.width, props.height, props.title.c_str(), nullptr, nullptr);
        
        _graphicsContext = new OpenGLContext(_glfwWindow);
        _graphicsContext->Init();
        glfwSetWindowUserPointer(_glfwWindow, &_pointerData);

        SetSync(true);

        glfwSetWindowSizeCallback(_glfwWindow, [](GLFWwindow* window, int width, int height)
            {
                PointerData& data = *(PointerData*)glfwGetWindowUserPointer(window);
                data.width = width;
                data.height = height;
                WindowResizeEvent event(width, height);
                data.callback(event);
            }
        );

        glfwSetWindowCloseCallback(_glfwWindow, [](GLFWwindow* window) 
            {
                PointerData& data = *(PointerData*)glfwGetWindowUserPointer(window);
                WindowCloseEvent event;
                data.callback(event);
            }
        );

        glfwSetKeyCallback(_glfwWindow, [](GLFWwindow* window, int key, int scancode, int action, int mods)
            {
                PointerData& data = *(PointerData*)glfwGetWindowUserPointer(window);
                switch (action)
                {
                    case GLFW_PRESS:
                    {
                        KeyPressedEvent event(key, 0);
                        data.callback(event);
                        break;
                    }
                    case GLFW_RELEASE:
                    {
                        KeyReleasedEvent event(key);
                        data.callback(event);
                        break;
                    }
                    case GLFW_REPEAT:
                    {
                        KeyPressedEvent event(key, 1);
                        data.callback(event);
                        break;
                    }
                }
            }
        );

        glfwSetMouseButtonCallback(_glfwWindow, [](GLFWwindow* window, int button, int action, int mods)
            {
                PointerData& data = *(PointerData*)glfwGetWindowUserPointer(window);
                switch (action)
                {
                    case GLFW_PRESS:
                    {
                        MouseButtonPressedEvent event(button);
                        data.callback(event);
                        break;
                    }
                    case GLFW_RELEASE:
                    {
                        MouseButtonReleasedEvent event(button);
                        data.callback(event);
                        break;
                    }
                }
            }
        );

        glfwSetScrollCallback(_glfwWindow, [](GLFWwindow* window, double xOffset, double yOffset)
            {
                PointerData& data = *(PointerData*)glfwGetWindowUserPointer(window);
                MouseScrolledEvent event((float)xOffset, (float)yOffset);
                data.callback(event);
            }
        );

        glfwSetCursorPosCallback(_glfwWindow, [](GLFWwindow* window, double xPos, double yPos)
            {
                PointerData& data = *(PointerData*)glfwGetWindowUserPointer(window);
                MouseMovedEvent event((float)xPos, (float)yPos);
                data.callback(event);
            }
        );
    }

    void WindowsWindow::OnUpdate()
    {
        glfwPollEvents();
        _graphicsContext->SwapBuffers();
    }

    void WindowsWindow::Shutdown()
    {
        glfwDestroyWindow(_glfwWindow);
    }

    void WindowsWindow::SetSync(bool enable)
    {
        if (enable)
        {
            glfwSwapInterval(1);
        }
        else
        {
            glfwSwapInterval(0);
        }
    }
}
